# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: 绑定解绑_M40Pro_打包版_米家10.6.707.py
# Bytecode version: 3.12.0rc2 (3531)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)

import time
import os
import sys
import threading
import logging
import random
from asyncio import timeout
from datetime import datetime
import uiautomator2 as u2
import serial
import openpyxl
file_name_MCU = datetime.now().strftime('%Y%m%d%H%M') + '_MCU.log'
app_pgname = 'com.xiaomi.smarthome'
title_list = ['测试次数', '本轮开始时间', '绑定结果', '异常备注']
resourceid_add = 'com.xiaomi.smarthome:id/gq'
resourceid_find = 'com.xiaomi.smarthome:id/ceh'
resourceid_seng_lockname = 'com.xiaomi.smarthome:id/dhb'
resourceid_first_result_lockname = 'com.xiaomi.smarthome:id/cjm'
resourceid_first_result_lockname_in = 'com.xiaomi.smarthome:id/m5'
resourceid_safe_mun = 'com.xiaomi.smarthome:id/f3y'
resourceid_match_num1 = 'com.xiaomi.smarthome:id/cri'
resourceid_match_num2 = 'com.xiaomi.smarthome:id/crj'
resourceid_match_num3 = 'com.xiaomi.smarthome:id/crk'
resourceid_match_num4 = 'com.xiaomi.smarthome:id/crl'
resourceid_match_num5 = 'com.xiaomi.smarthome:id/crm'
resourceid_match_num6 = 'com.xiaomi.smarthome:id/crn'
xpath_add_pwd = '//*[@resource-id=\"com.xiaomi.smarthome:id/ayk\"]/android.widget.FrameLayout[2]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[2]/android.view.ViewGroup[1]'

class BindingUnbind(object):
    def __init__(self):
        self.d = u2.connect(devices_id)
        self.__create_excel()
        self.log = open(os.path.dirname(sys.argv[0]) + '/' + file_name_MCU, 'a', encoding='utf-8', errors='replace')
        self.ser1 = BindingUnbind.serial_port1()
        self.ser2 = BindingUnbind.serial_port2()
        self.running = True
        self.times = None
        self.start_time = None
        self.error_log = None
        self.fail_times = 0
        self.result = None
        self.pass_times = 0
        self.block_times = 0
        self.result_dev_add = None
        self.logger = BindingUnbind.__create_file()

    @staticmethod
    def __create_file():
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.DEBUG)
        log_format = '%(asctime)s - %(levelname)s - %(message)s'
        file_name = datetime.now().strftime('%Y%m%d%H%M') + '绑定解绑.log'
        file_handler = logging.FileHandler(os.path.dirname(sys.argv[0]) + '/' + file_name)
        file_handler.setFormatter(logging.Formatter(log_format))
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(logging.Formatter(log_format))
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)
        return logger

    @staticmethod
    def __get_time(get_time):
        hms = get_time.strftime('%H:%M:%S')
        return hms

    @staticmethod
    def __get_time1(get_time):
        ymdhms = get_time.strftime('%Y%m%d%H%M%S')
        return ymdhms

    @staticmethod
    def __get_time2(get_time):
        hms = get_time.strftime('%H%M%S')
        return hms

    @staticmethod
    def __count_time(end_time, st_time):
        use_time = (end_time - st_time).total_seconds()
        return float(use_time)

    def __create_excel(self):
        """创建数据表格和截图文件夹"""  # inserted
        self.excel_path = os.path.dirname(sys.argv[0]) + '/绑定解绑测试数据' + str(BindingUnbind.__get_time1(datetime.now())) + '.xlsx'
        self.picture_path = os.path.dirname(sys.argv[0]) + '/绑定解绑异常截图' + str(BindingUnbind.__get_time1(datetime.now()))
        self.picture_path1 = self.picture_path + '/其它异常'
        os.makedirs(self.picture_path, exist_ok=True)
        os.makedirs(self.picture_path1, exist_ok=True)
        excel_makedirs = openpyxl.Workbook()
        excel_makedirs.save(self.excel_path)
        self.excel = openpyxl.load_workbook(self.excel_path)
        self.excel_sheet = self.excel.active
        self.excel_sheet.auto_filter.ref = 'A1:G1'
        self.excel_sheet.append(title_list)
        self.excel_sheet['K1'] = '测试总次数'
        self.excel_sheet['L1'] = '绑定成功次数'
        self.excel_sheet['M1'] = '绑定失败次数'
        self.excel_sheet['N1'] = '绑定未知异常次数（block）'
        self.excel_sheet['O1'] = '绑定成功率'
        self.excel.save(self.excel_path)

    @staticmethod
    def serial_port1():
        ser1 = serial.Serial(port=port_mcu, baudrate=115200, timeout=1)
        if not ser1 or not ser1.is_open:
            ser1.open()
        return ser1

    @staticmethod
    def serial_port2():
        ser2 = serial.Serial(port=port_relay, baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
        if not ser2 or not ser2.is_open:
            ser2.open()
        return ser2

    def send_to_serial(self, command):
        """前板ser1发送命令到串口"""  # inserted
        try:
            byte_data = bytes.fromhex(command)
            self.ser1.write(byte_data)
        except Exception as e:
            self.logger.error(f'Error sending command: {str(e)}')

    def send_command(self, command, delay=0.1):
        """\n        发送命令到继电器，被relaytest方法调用\n        :param command: 字符串或字节类型的命令\n        :param delay: 发送后等待时间（秒）\n        """  # inserted
        if isinstance(command, str):
            cmd_bytes = command.encode('utf-8')
        else:  # inserted
            cmd_bytes = command
        self.ser2.write(cmd_bytes)
        time.sleep(delay)

    def relaytest(self, end, long_or_click):
        try:
            self.send_command(end)
            time.sleep(long_or_click)
            self.send_command('O')
        except Exception as e:
            self.logger.error(f'继电器测试异常,{str(e)}')
        else:  # inserted
            pass

    def click_center1(self, element):
        if self.d(text=element).exists(timeout=10):
            x, y = self.d(text=element).center()
            self.d.click(x, y)

    def click_center2(self, element):
        if self.d(resourceId=element).exists(timeout=10):
            x, y = self.d(resourceId=element).center()
            self.d.click(x, y)

    def num_in(self):
        if self.d(text='添加密码').exists(timeout=10):
            self.logger.info('识别到元素：添加密码')
            self.d.xpath(xpath_add_pwd).set_text('147258')
            self.logger.info('键入密码：147258')
            time.sleep(1)
            self.click_center1('下一步')
            self.logger.info('点击下一步')
            time.sleep(1)
        else:  # inserted
            self.logger.info('未识别到元素：添加密码')

    def read_from_serial(self):
        """前板持续读取串口数据并写入日志文件"""  # inserted
        while self.running:
            try:
                if self.ser1.in_waiting > 0:
                    data = self.ser1.read(self.ser1.in_waiting)
                    decoded_data = data.decode('utf-8', errors='replace').strip()
                    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:(-3)]
                    self.log.write(f'[{timestamp}] {decoded_data}\n')
                    self.log.flush()
                    time.sleep(0.01)
            except Exception as e:
                pass  # postinserted
            self.logger.error(f'Error reading serial: {str(e)}')
            return None

    def code_in(self):
        read_thread = threading.Thread(target=self.read_from_serial)
        read_thread.start()
        for times in range(test_cycles):
            wait_time = random.randint(wait_time_a, wait_time_b)
            self.times = times
            self.start_time = datetime.now()
            self.error_log = ''
            self.logger.info('*********************开始第%s轮测试，时间：%s********************' % (self.times + 1, BindingUnbind.__get_time(datetime.now())))
            try:
                self.relaytest('K', long_or_click=5)
                self.logger.info('长按重置键操作完成')
                if self.times > 0 and self.result_dev_add == 1:
                    if self.d(text='删除设备').exists(timeout=10):
                        self.logger.info('找到元素：删除设备')
                    else:  # inserted
                        self.logger.info('10s内未找到元素：删除设备')
                        self.d.screenshot(self.picture_path1 + '/第%s次未找到删除设备%s.png' % (str(self.times + 1), str(BindingUnbind.__get_time2(datetime.now()))))
                    self.result_dev_add = 0
                time.sleep(6)
                self.d.app_stop(app_pgname)
                self.d.press('back')
                self.logger.info('按返回键，防止桌面异常')
                time.sleep(1)
                self.d.app_start(app_pgname, use_monkey=True, wait=True)
                if self.d(textMatches=f'.*{str(lock_name1)}').exists(timeout=15):
                    self.logger.info('找到指定门锁设备')
                    time.sleep(2)
                    self.d(textMatches=f'.*{str(lock_name1)}').click()
                    self.logger.info('点击指定门锁设备')
                    if self.d(text='删除设备').exists(timeout=60):
                        self.click_center1('删除设备')
                        self.logger.info('删除插件设备')
                        time.sleep(10)
                    else:  # inserted
                        self.logger.info('未出现删除提示，重新开始下一轮测试')
                        self.result = 'Fail'
                        self.error_log = '未出现删除信息提示'
                        self.fail_times += 1
                        self.handle_error(2)
            except Exception as e:
                    else:  # inserted
                        continue
                else:  # inserted
                    if self.result == 'Pass':
                        self.logger.info('绑定成功后主页面没有找到指定门锁')
                        self.result = 'Fail'
                        self.error_log = '绑定后主页没有门锁插件'
                        self.d.screenshot(self.picture_path1 + '/第%s次测试绑定严重异常.png' % str(self.times + 1))
                        self.logger.info('没有找到指定门锁，开始添加门锁')
                time.sleep(2)
                if self.d(resourceId=resourceid_add).exists(timeout=20):
                    self.d(resourceId=resourceid_add).click()
                    self.logger.info('点击+元素')
                else:  # inserted
                    self.logger.info('未找到+元素')
                self.click_center1('添加设备')
                self.logger.info('点击添加设备')
                time.sleep(1.5)
                self.d(resourceId=resourceid_find).click()
                self.logger.info('点击搜索图标')
                time.sleep(1.5)
                self.d(resourceId=resourceid_seng_lockname).send_keys(lock_name)
                self.logger.info('输入门锁名称')
                time.sleep(1.5)
                if self.d(text=lock_name, resourceId=resourceid_first_result_lockname).exists(timeout=20):
                    self.logger.info('搜索到指定门锁')
                    time.sleep(0.5)
                    self.d.press('back')
                    time.sleep(1)
                    self.click_center2(resourceid_first_result_lockname_in)
                    time.sleep(3)
                    self.d(text='设备已重置').click()
                    self.logger.info('点击设备已重置')
                    break
                self.logger.info('未识别到指定门锁，本轮测试失败，等待%s秒后开始下一轮测试' % wait_time)
                self.result = 'Block'
                self.error_log = '未识别到指定门锁'
                self.block_times += 1
                self.handle_error(2)
                time.sleep(wait_time)
            else:  # inserted
                continue
                self.relaytest('M', long_or_click=5)
                self.logger.info('长按配对键操作完成')
                if self.d(text='下一步').exists(timeout=30):
                    self.logger.info('识别到下一步')
                    time.sleep(1)
                    self.d(text='下一步').click()
                    break
                self.logger.info('未识别到下一步，本轮测试失败，等待%s秒后开始下一轮测试' % wait_time)
                self.result = 'Fail'
                self.error_log = '未识别到下一步'
                self.fail_times += 1
                self.handle_error(2)
                time.sleep(wait_time)
            else:  # inserted
                continue
                if self.d(text='设置安全密码').exists(timeout=20):
                    for i in range(1, 13):
                        self.d(resourceId=resourceid_safe_mun).click()
                        time.sleep(0.2)
                    break
                else:  # inserted
                    self.logger.info('未识别到设置安全码，本轮测试失败，等待%s秒后开始下一轮测试' % wait_time)
                    self.result = 'Fail'
                    self.error_log = '未识别到设置安全码'
                    self.fail_times += 1
                    self.handle_error(2)
                    time.sleep(wait_time)
                else:  # inserted
                    continue
                self.logger.info('输完安全码')
                if self.d(text='验证配对码').exists(timeout=20):
                    a = self.d(resourceId=resourceid_match_num1).get_text()
                    b = self.d(resourceId=resourceid_match_num2).get_text()
                    c = self.d(resourceId=resourceid_match_num3).get_text()
                    d = self.d(resourceId=resourceid_match_num4).get_text()
                    e = self.d(resourceId=resourceid_match_num5).get_text()
                    f = self.d(resourceId=resourceid_match_num6).get_text()
                    time.sleep(1)
                    number = '0F DE 00 81 00 08 00 3' + str(a) + ' 3' + str(b) + ' 3' + str(c) + ' 3' + str(d) + ' 3' + str(e) + ' 3' + str(f) + ' 23 01 04'
                    self.logger.info('识别到配对码：%s%s%s%s%s%s' % (str(a), str(b), str(c), str(d), str(e), str(f)))
                    self.send_to_serial(command=number)
                    self.logger.info('输入配对码')
                    break
                self.logger.info('未识别到验证配对码元素，本轮测试失败，等待%s秒后开始下一轮测试' % wait_time)
                self.result = 'Fail'
                self.error_log = '未识别到验证码'
                self.fail_times += 1
                self.handle_error(2)
                time.sleep(wait_time)
            else:  # inserted
                continue
                if self.d(text='设备添加成功').wait(timeout=60):
                    self.logger.info('设备添加成功')
                    time.sleep(5)
                    self.d(text='客厅').click()
                    time.sleep(2)
                    self.d(text='下一步').click()
                    time.sleep(2)
                    self.d(text='下一步').click()
                    if self.d(text='同意').exists(timeout=30):
                        self.logger.info('识别到元素：同意')
                        time.sleep(0.5)
                        self.d(text='同意').click()
                        self.logger.info('点击同意')
                        self.result_dev_add = 1
                        break
                    self.logger.info('未识别到元素:同意，本轮测试失败，等待%s秒后开始下一轮测试' % wait_time)
                    self.result = 'Fail'
                    self.error_log = '未识别到元素:同意'
                    self.fail_times += 1
                    self.handle_error(2)
                    time.sleep(wait_time)
                else:  # inserted
                    continue
                    if self.d(textContains='无法上锁').exists(timeout=40):
                        time.sleep(1)
                        self.click_center1('添加用户')
                        self.logger.info('点击添加用户')
                        if self.d(text='蓝牙未连接').exists(timeout=2):
                            self.logger.info('蓝牙未连接,等待6秒蓝牙连接')
                            time.sleep(6)
                            self.click_center1('添加用户')
                            self.logger.info('点击添加用户')
                            if self.d(text='蓝牙未连接').exists(timeout=2):
                                self.logger.info('蓝牙未连接,等待6秒蓝牙连接')
                                time.sleep(6)
                                self.click_center1('添加用户')
                                self.logger.info('点击添加用户')
                                if self.d(text='蓝牙未连接').exists(timeout=2):
                                    self.logger.info('蓝牙未连接,等待6秒蓝牙连接')
                                    time.sleep(6)
                                    self.click_center1('添加用户')
                                    self.logger.info('点击添加用户')
                                    if self.d(text='蓝牙未连接').exists(timeout=2):
                                        self.logger.info('蓝牙未连接,等待6秒蓝牙连接')
                                        time.sleep(6)
                                        self.click_center1('添加用户')
                                        self.logger.info('点击添加用户')
                                        if self.d(text='蓝牙未连接').exists(timeout=2):
                                            self.logger.info('蓝牙未连接,等待6秒蓝牙连接')
                                            time.sleep(6)
                                            self.click_center1('添加用户')
                                            self.logger.info('点击添加用户')
                        num = '147258'
                        time.sleep(2)
                        self.num_in()
                        self.num_in()
                        if self.d(text='添加密码成功').wait(timeout=20):
                            self.pass_times += 1
                            self.result = 'Pass'
                            self.handle_error(1)
                            self.d.press('back')
                            self.logger.info('添加密码成功,本轮测试结束，等待%s秒后开始下一轮' % wait_time)
                            time.sleep(wait_time)
                        else:  # inserted
                            self.fail_times += 1
                            self.result = 'Fail'
                            self.error_log = '未找到元素：添加密码成功'
                            self.handle_error(2)
                            self.logger.info('未找到添加密码成功元素，本轮测试失败')
                            self.logger.info('本轮测试结束，等待%s秒后开始下一轮' % wait_time)
                            time.sleep(wait_time)
                    else:  # inserted
                        self.logger.info('40s内蓝牙未连接，本轮测试失败')
                        self.logger.info('本轮测试结束，等待%s秒后开始下一轮' % wait_time)
                        self.fail_times += 1
                        self.result = 'Fail'
                        self.error_log = '蓝牙连接超时'
                        self.handle_error(2)
                        time.sleep(wait_time)
                self.logger.info('60s内未找到元素：设备添加成功，本轮测试失败')
                self.logger.info('本轮测试结束，等待%s秒后开始下一轮' % wait_time)
                self.fail_times += 1
                self.result = 'Fail'
                self.error_log = '设备添加失败'
                self.handle_error(2)
                time.sleep(wait_time)
        else:  # inserted
            self.running = False
            self.logger.error('####第%s次测试，流程异常' % (self.times + 1))
            self.logger.error(f'测试流程异常: {str(e)}')
            self.result = 'Fail'
            self.d.screenshot(self.picture_path1 + '/第%s次测试异常%s.png' % (str(self.times + 1), str(BindingUnbind.__get_time2(datetime.now()))))
            self.logger.info('已完成截图,第%s次测试异常' % str(self.times + 1))
            time.sleep(5)

    def excel_append(self, times, start_time, result, error_log):
        log_list = [times, start_time, result, error_log]
        self.excel_sheet.append(log_list)
        self.excel_sheet['K2'] = self.times + 1
        self.excel_sheet['L2'] = self.pass_times
        self.excel_sheet['M2'] = self.fail_times
        self.excel_sheet['N2'] = self.block_times
        self.excel_sheet['O2'] = '=(L2/K2)'
        self.excel_sheet['O2'].number_format = '0.00%'
        self.excel.save(self.excel_path)

    def handle_error(self, state):
        if state == 1:
            self.excel_append(self.times + 1, BindingUnbind.__get_time(self.start_time), self.result, '')
        else:  # inserted
            if state == 2:
                self.d.screenshot(self.picture_path + '/第%s次测试异常%s.png' % (str(self.times + 1), str(BindingUnbind.__get_time2(datetime.now()))))
                self.logger.info('已完成截图,第%s次测试异常' % str(self.times + 1))
                self.excel_append(self.times + 1, BindingUnbind.__get_time(self.start_time), self.result, self.error_log)
if __name__ == '__main__':
    test_cycles = int(input('输入测试次数:'))
    devices_id = input('输入手机设备id，cmd中输入adb devices (仅连接一台设备时可不填):')
    port_mcu = input('输入前板MCU串口号，如：COM9:')
    port_relay = input('输入继电器串口号，如：COM12:')
    print('APP主页检测门锁关键字\'Pro\',请勿出现结尾为\'Pro\'的门锁')
    lock_name = input('输入门锁设备名称，如（小米智能门锁 M40 Pro)：')
    lock_name1 = lock_name.split()[2]
    print('输入随机等待时长的最短最长时间，若不需要随机，可输入两个相同整数')
    wait_time_a = int(input('输入一轮结束后的最短等待时间'))
    wait_time_b = int(input('输入一轮结束后的最长等待时间'))
    a = BindingUnbind()
    a.code_in()